﻿#include "pch.h"
#include "sbhs.h"
#include "sbctn.h"
#include "kh_compiler.h"
#include "kh_code.h"
#include "extend/sberror.h"
#include "extend/sbmnehs.h"

//////////////////////////////////////////////////////////////////////////
// 컴파일러

//
K_NODELIST_DECL(khFuncList, khFuncData);

//
static struct khCompilerImpl
{
	kint				error;

	kint				cloop;
	kint				cswit;
	enum khScope		cscope;
	khFuncData*			cfunc;

	khPatchArr			patchs;
	khLoopArr			loops;
	khSwitchArr			swits;
	khFuncList			funcs;
} kh_impl_cmpl =
{
	0,

	0,
	0,
	KHSCP_UNKNOWN,
	NULL,

	{NULL, 0, 0, },
	{NULL, 0, 0, },
	{NULL, 0, 0, },
	{NULL, NULL, 0, }
};

// 초기화
void _kh_compiler_init(void)
{
	k_arr_init(khPatchArr, &kh_impl_cmpl.patchs, 0);
	k_arr_init(khLoopArr, &kh_impl_cmpl.loops, 0);
	k_arr_init(khSwitchArr, &kh_impl_cmpl.swits, 0);
	k_nodelist_init(khFuncList, &kh_impl_cmpl.funcs);
}

// 제거
void _kh_compiler_disp(void)
{
	k_nodelist_disp_cb(khFuncList, &kh_impl_cmpl.funcs, kh_fd_delete);
	k_arr_disp(khSwitchArr, &kh_impl_cmpl.swits);
	k_arr_disp(khLoopArr, &kh_impl_cmpl.loops);
	k_arr_disp(khPatchArr, &kh_impl_cmpl.patchs);
}

// 껌팔기 오류
void kh_cmpl_error(kcham ew, khToken* token, kint code, const char* fmt, ...)
{
	char* psz;
	va_list va;
	va_start(va, fmt);
	k_vasprintf(&psz, fmt, va);
	va_end(va);
	k_debug_error(ew, token->column, token->line, khs_filename, code, "compile", psz);
	k_delete(psz);
}


//////////////////////////////////////////////////////////////////////////
// 껌팔자

//
static kcham _kh_cmpl_compile(khToken* token, khCodeData* code, kbool sib);
static kcham _kh_cmpl_finalize_func(khFuncData* fd);

// 현재 함수
static khFuncData* _kh_cmpl_cur_func()
{
	return kh_impl_cmpl.cfunc;
}

// 함수 넣기
static void _kh_cmpl_push_func()
{
	if (kh_impl_cmpl.cfunc)
	{
		if (kh_impl_cmpl.cfunc != k_nodelist_last(&kh_impl_cmpl.funcs))
		{
			kh_impl_cmpl.cfunc = kh_impl_cmpl.cfunc->prev;	// 이거 prev인지 next인지 확인 -> prev가 맞는 듯
			kh_fd_reset(kh_impl_cmpl.cfunc, FALSE);
		}
		else
		{
			kh_impl_cmpl.cfunc = kh_fd_new();
			k_nodelist_append(khFuncList, &kh_impl_cmpl.funcs, kh_impl_cmpl.cfunc);
		}
	}
	else
	{
		if (k_nodelist_is_have(&kh_impl_cmpl.funcs))
		{
			kh_impl_cmpl.cfunc = k_nodelist_first(&kh_impl_cmpl.funcs);
			kh_fd_reset(kh_impl_cmpl.cfunc, FALSE);
		}
		else
		{
			kh_impl_cmpl.cfunc = kh_fd_new();
			k_nodelist_append(khFuncList, &kh_impl_cmpl.funcs, kh_impl_cmpl.cfunc);
		}
	}

	kh_impl_cmpl.cfunc->filename = khs_filename;
}

// 함수 뽑기
static void _kh_cmpl_pop_func()
{
	if (kh_impl_cmpl.cfunc)
		kh_impl_cmpl.cfunc = kh_impl_cmpl.cfunc->next;
}

// 현재 루프
static struct khLoopData* _kh_cmpl_cur_loop()
{
	return kh_impl_cmpl.cloop < 0 ? NULL : &k_arr_nth(&kh_impl_cmpl.loops, kh_impl_cmpl.cloop);
}

// 루프 넣기
static struct khLoopData* _kh_cmpl_push_loop(enum khLoop stmt)
{
	struct khLoopData lp;
	lp.stmt = stmt;
	lp.brk = -1;
	lp.ctn = -1;
	kh_impl_cmpl.cloop = (kint)k_arr_count(&kh_impl_cmpl.loops);
	k_arr_add(khLoopArr, &kh_impl_cmpl.loops, lp);
	return &k_arr_inv(&kh_impl_cmpl.loops, 0);
}

// 루프 뽑기
static void _kh_cmpl_pop_loop()
{
	kssize_t cnt;
	k_arr_remove_nth(khLoopArr, &kh_impl_cmpl.loops, k_arr_count(&kh_impl_cmpl.loops) - 1);
	cnt = k_arr_count(&kh_impl_cmpl.loops);
	kh_impl_cmpl.cloop = cnt == 0 ? -1 : (kint)cnt - 1;
}

// 패치 넣기
static kint _kh_cmpl_add_patch(kint addr, kint next)
{
	kssize_t cnt = k_arr_count(&kh_impl_cmpl.patchs);
	struct khPatchData pd;
	pd.addr = addr;
	pd.next = next;
	k_arr_add(khPatchArr, &kh_impl_cmpl.patchs, pd);
	return (kint)cnt;
}

// 패치 적용
static void _kh_cmpl_apply_patch(kint patch, khCodeData* code, kint data)
{
	kint save = kh_cd_get_loc(code);

	if (patch >= 0)
	{
		struct khPatchData* pd = &k_arr_nth(&kh_impl_cmpl.patchs, patch);
		kh_cd_set_loc(code, pd->addr);
		kh_cd_emit(code, 0, data);
		patch = pd->next;
	}

	kh_cd_set_loc(code, save);
}

// 현재 스위치
static struct khSwitchData* _kh_cmpl_cur_switch()
{
	return kh_impl_cmpl.cswit < 0 ? NULL : &k_arr_nth(&kh_impl_cmpl.swits, kh_impl_cmpl.cswit);
}

// 스위치 넣기
static struct khSwitchData* _kh_cmpl_push_switch(kint addr, kbool defonly)
{
	struct khSwitchData sd;
	sd.tbl = 0;
	sd.addr = addr;
	sd.onlydef = defonly;
	kh_impl_cmpl.cswit = (kint)k_arr_count(&kh_impl_cmpl.swits);
	k_arr_add(khSwitchArr, &kh_impl_cmpl.swits, sd);
	return &k_arr_inv(&kh_impl_cmpl.swits, 0);
}

// 스위치 팝
static void _kh_cmpl_pop_switch()
{
	kssize_t cnt = k_arr_count(&kh_impl_cmpl.swits) - 1;
	k_arr_remove_nth(khSwitchArr, &kh_impl_cmpl.swits, cnt);
	kh_impl_cmpl.cswit = cnt == 0 ? -1 : (kint)cnt - 1;
}

// 할당 경고
static void _kh_cmpl_check_assign(khToken* token, kint offset)
{
	if (!kh_fd_var_mark(kh_impl_cmpl.cfunc, offset))
	{
		// 할당한 적이 없으면 경고
		kh_cmpl_error(FALSE, token, KERR_EMPTY, "'%s' is not assigned. [0x%p]", token->value.str, token->value.str);
	}
}

// 수식 검사
static kcham _kh_cmpl_check_expr(khToken* token, kcham display_error)
{
	if (token->flags & KHTKF_EXPR_SELF)
		return TRUE;
	else if (token->parent && token->parent->flags & KHTKF_EXPR_PARENT)
		return TRUE;
	else
	{
		// 의미 없는 수식
		if (display_error)
			kh_cmpl_error(TRUE, token, KERR_NOMEAN, "expression has no mean, no effect.");

		return FALSE;
	}
}

// 식별자
static kcham _kh_cmpl_type_identifier(khToken* token, khCodeData* code)
{
	struct khVarData* vd;
	kuint sym;
	kint offset;

	if (!_kh_cmpl_check_expr(token, TRUE))
		return FALSE;

	switch (token->role)
	{
		case KHTKR_ID_PUBLIC:
			sym = kh_fd_const_sym(_kh_cmpl_cur_func(), token->value.str);
			kh_cd_emit_sym(code, token->line, KHMNE_LDPUB, sym);
			break;

		case KHTKR_ID_SELF:
			sym = kh_fd_const_sym(_kh_cmpl_cur_func(), token->value.str);
			kh_cd_emit_sym(code, token->line, KHMNE_LDMEM, sym);
			break;

		case KHTKR_ID_LOCAL:
			offset = kh_fd_var_get(_kh_cmpl_cur_func(), KHSCP_LOCAL, token, &vd);

			if (offset < 0)
			{
				// 지역 변수가 없다
				kh_cmpl_error(TRUE, token, KERR_EMPTY, "local variable '%s' is not defined.",
					token->value.str);
				return FALSE;
			}

			if (!vd->asgn)
			{
				// 할당 경고
				kh_cmpl_error(FALSE, token, KERR_NOMEAN, "local variable '%s' is not assigned.",
					token->value.str);
				kh_fd_var_mark(_kh_cmpl_cur_func(), offset);
			}

			kh_cd_emit_op(code, token->line, KHMNE_LDLOC, offset);
			break;

		default:
			offset = kh_fd_var_get(_kh_cmpl_cur_func(), KHSCP_UNKNOWN, token, &vd);

			if (offset >= 0)
			{
				// 지역 변수
				if (!vd->asgn)
				{
					// 할당 경고
					kh_cmpl_error(FALSE, token, KERR_NOMEAN, "local variable '%s' is not assigned.", token->value.str);
					kh_fd_var_mark(_kh_cmpl_cur_func(), offset);
				}

				kh_cd_emit_op(code, token->line, KHMNE_LDLOC, offset);
			}
			else if (offset == KHSCP_SELF)
			{
				// 셀프
				sym = kh_fd_const_sym(_kh_cmpl_cur_func(), token->value.str);
				kh_cd_emit_sym(code, token->line, KHMNE_LDMEM, sym);
			}
			else if (offset == KHSCP_PUBLIC)
			{
				// 퍼블릭
				sym = kh_fd_const_sym(_kh_cmpl_cur_func(), token->value.str);
				kh_cd_emit_sym(code, token->line, KHMNE_LDPUB, sym);
			}
			else
			{
				// 변수가 없다 -> 전역으로
				if (offset != KHSCP_UNKNOWN)
				{
					kh_cmpl_error(TRUE, token, KERR_INVAL, "invalid variable offset scope!");
					return FALSE;
				}

				sym = kh_fd_const_sym(_kh_cmpl_cur_func(), token->value.str);
				kh_fd_var_set(_kh_cmpl_cur_func(), KHSCP_PUBLIC, token, TRUE, TRUE);
				kh_cd_emit_sym(code, token->line, KHMNE_LDPUB, sym);
			}

			break;
	}

	return TRUE;
}

// 상수
static kcham _kh_cmpl_type_constant(khToken* token, khCodeData* code)
{
	kany av;
	kint role;

	role = token->role;

	switch (role)
	{
		case KHTKR_CONST_NULL:
			kh_cd_emit(code, token->line, KHMNE_LDNULL);
			break;

		case KHTKR_CONST_BOOL:
			kh_cd_emit(code, token->line, token->value.i64 != 0 ? KHMNE_LDTRUE : KHMNE_LDFALSE);
			break;

		case KHTKR_CONST_INT:
			if (token->value.i64 < 0 || token->value.i64 > 8)
			{
				av.i = (kint)token->value.i64;
				kh_cd_emit_32(code, token->line, KHMNE_LDC_I4, av.i);
			}
			else
			{
				av.i = (kint)token->value.i64 + KHMNE_LDC_I_0;
				kh_cd_emit(code, token->line, av.u);
			}

			break;

		case KHTKR_CONST_LONG:
			kh_cd_emit_64(code, token->line, KHMNE_LDC_I8, token->value.i64);
			break;

		case KHTKR_CONST_FLOAT:
			av.f = (float)token->value.dbl;
			kh_cd_emit_32(code, token->line, KHMNE_LDC_R4, av.i);
			break;

		case KHTKR_CONST_DOUBLE:
			kh_cd_emit_64(code, token->line, KHMNE_LDC_R8, token->value.i64);
			break;

		case KHTKR_CONST_STRING:
			if (token->flags & KHTKF_SYMBOL)
			{
				av.u = kh_fd_const_sym(_kh_cmpl_cur_func(), token->value.str);
				kh_cd_emit_sym(code, token->line, KHMNE_LDSYM, av.u);
			}
			else
			{
				av.i = kh_fd_const_str(_kh_cmpl_cur_func(), token->value.str);
				kh_cd_emit_addr(code, token->line, KHMNE_LDSTR, av.i);
			}

			break;

		case KHTKR_CONST_WIDESTRING:
			av.i = kh_fd_const_wcs(_kh_cmpl_cur_func(), token->value.wcs);
			kh_cd_emit_addr(code, token->line, KHMNE_LDWCS, av.i);
			break;
	}

	return TRUE;
}

// 선언
static kcham _kh_cmpl_type_declaration(khToken* token, khCodeData* code)
{
	enum khScope scope, prev;
	struct khVarData* vd;

	switch (token->role)
	{
		case KHTKR_DECL_LOCAL:
			scope = KHSCP_LOCAL;
			break;

		case KHTKR_DECL_PUBLIC:
			scope = KHSCP_PUBLIC;
			break;

		case KHTKR_DECL_SELF:
			scope = KHSCP_SELF;
			break;

		default:
			return FALSE;
	}

	prev = (enum khScope)kh_fd_var_get(_kh_cmpl_cur_func(), KHSCP_UNKNOWN, token->child[0], &vd);

	if (prev == KHSCP_UNKNOWN)
	{
		kh_fd_var_set(_kh_cmpl_cur_func(), scope, token->child[0], TRUE, FALSE);

		if (token->child[1] && !_kh_cmpl_compile(token->child[1], code, TRUE))
		{
			// 할당이 있을 경우 할당 수행 후 오류 처리 해줌
			return FALSE;
		}

		return TRUE;
	}
	else
	{
		if (prev == scope || (prev >= 0 && scope == KHSCP_LOCAL))
			return TRUE;
		else
		{
			kh_cmpl_error(TRUE, token, KERR_INVAL, "'%s' is declared %s as %s at line %d.",
				token->child[0]->value.str, (vd->impl) ? "implicity" : "explicity",
				kh_tos_scope(vd->off), vd->token->line);
			return FALSE;
		}
	}
}

// 연산자 할당 '='
static kcham _kh_cmpl_op_assign(khToken* token, khCodeData* code)
{
	kuint sym;
	kint work, offset;
	khToken* left;

	// 넣을 곳 검사
	work = -1;
	left = token->child[0];

	if (left->type == KHTKT_OP)
	{
		if (left->role == KHTKR_OP_DOT)
		{
			if (!_kh_cmpl_compile(left->child[0], code, TRUE))
				return FALSE;

			work = 0;
		}
		else if (left->role == KHTKR_OP_INDEX)
		{
			if (!_kh_cmpl_compile(left->child[0], code, TRUE) ||
				!_kh_cmpl_compile(left->child[1], code, TRUE))
				return FALSE;

			work = 1;
		}
	}
	else if (left->type == KHTKT_IDENTIFIER)
	{
		work = 2;
	}

	// 먼저 DUP 검사
	if (token->flags & KHTKF_DUP)
	{
		// 자식에게 복제하라고 전함 -> 후방 증감 연산자용
		if (_kh_cmpl_check_expr(token, FALSE))
			token->child[1]->flags = (enum khTokenFlag)(token->child[1]->flags | KHTKF_DUP);

		// 체크 안하고 값
		if (!_kh_cmpl_compile(token->child[1], code, TRUE))
			return FALSE;
	}
	else
	{
		// 먼저 넣을 값 만들고
		if (!_kh_cmpl_compile(token->child[1], code, TRUE))
			return FALSE;

		// 혹시 이게 식 중간에 있으면
		if (_kh_cmpl_check_expr(token, FALSE))
			kh_cd_emit(code, token->line, KHMNE_DUP);
	}

	// 넣을 곳 완성
	if (work == 0)
	{
		// 점 연산
		sym = kh_fd_const_sym(_kh_cmpl_cur_func(), left->child[1]->value.str);
		kh_cd_emit_sym(code, left->line, KHMNE_STFLD, sym);
	}
	else if (work == 1)
	{
		// 배열 기수
		kh_cd_emit(code, token->line, KHMNE_STELEM);
	}
	else if (work == 2)
	{
		// 그냥
		switch (left->role)
		{
			case KHTKR_ID_PUBLIC:
				sym = kh_fd_const_sym(_kh_cmpl_cur_func(), left->value.str);
				kh_cd_emit_sym(code, left->line, KHMNE_STPUB, sym);
				break;

			case KHTKR_ID_SELF:
				sym = kh_fd_const_sym(_kh_cmpl_cur_func(), left->value.str);
				kh_cd_emit_sym(code, left->line, KHMNE_STMEM, sym);
				break;

			case KHTKR_ID_LOCAL:
				offset = kh_fd_var_get(_kh_cmpl_cur_func(), KHSCP_LOCAL, left, NULL);

				if (offset == KHSCP_UNKNOWN)
				{
					// 변수가 없다. 만들어야 하는가? 중복되지 않도록 앞에서 만든다
					offset = kh_fd_var_set(_kh_cmpl_cur_func(), KHSCP_LOCAL, left, FALSE, FALSE);
				}

				// 그냥 마크 시킴
				kh_fd_var_mark(_kh_cmpl_cur_func(), offset);
				kh_cd_emit_op(code, token->line, KHMNE_STLOC, offset);
				break;

			default:
				offset = kh_fd_var_get(_kh_cmpl_cur_func(), KHSCP_UNKNOWN, left, NULL);

				if (offset >= KHSCP_LOCAL)
				{
					// 로컬
					kh_fd_var_mark(_kh_cmpl_cur_func(), offset);
					kh_cd_emit_op(code, token->line, KHMNE_STLOC, offset);
				}
				else if (offset == KHSCP_SELF)
				{
					// 셀프
					sym = kh_fd_const_sym(_kh_cmpl_cur_func(), left->value.str);
					kh_cd_emit_sym(code, left->line, KHMNE_STMEM, sym);
				}
				else if (offset == KHSCP_PUBLIC)
				{
					// 전역
					sym = kh_fd_const_sym(_kh_cmpl_cur_func(), left->value.str);
					kh_cd_emit_sym(code, left->line, KHMNE_STPUB, sym);
				}
				else
				{
					// 없다.. 지역 변수로
					if (offset != KHSCP_UNKNOWN)
						kh_cmpl_error(TRUE, left, KERR_INVAL, "invalid offset scope!");

					offset = kh_fd_var_set(_kh_cmpl_cur_func(), KHSCP_LOCAL, left, TRUE, TRUE);
					kh_fd_var_mark(_kh_cmpl_cur_func(), offset);
					kh_cd_emit_op(code, left->line, KHMNE_STLOC, offset);
				}

				break;
		}
	}
	else
	{
		// 뭐여
		kh_cmpl_error(TRUE, token, KERR_SYNTAX, "invalid l-value.");
		return FALSE;
	}

	return TRUE;
}

// 연산자 하나항 'unary'
static kcham _kh_cmpl_op_unary(khToken* token, khCodeData* code)
{
	kuint mne;

	if (!_kh_cmpl_compile(token->child[0], code, TRUE) ||
		!_kh_cmpl_check_expr(token, TRUE))
		return FALSE;

	switch (token->role)
	{
		case KHTKR_OP_UNARY_PLUS:
			mne = KHMNE_POS;
			break;

		case KHTKR_OP_UNARY_MINUS:
			mne = KHMNE_NEG;
			break;

		case KHTKR_OP_UNARY_SIG:
			mne = KHMNE_SIG;
			break;

		case KHTKR_OP_UNARY_NOT:
			mne = KHMNE_NOP;
			break;

		default:
			return FALSE;
	}

	kh_cd_emit(code, token->line, mne);

	return TRUE;
}

// 연산자 하나항 증감 'unary inc/dec'
static kcham _kh_cmpl_op_inc_dec(khToken* token, khCodeData* code)
{
	kuint mne;

	if (!_kh_cmpl_compile(token->child[0], code, TRUE))
		return FALSE;

	if (token->flags & KHTKF_DUP)
		kh_cd_emit(code, token->line, KHMNE_DUP);

	switch (token->role)
	{
		case KHTKR_OP_UNARY_INC:
			mne = KHMNE_INC;
			break;

		case KHTKR_OP_UNARY_DEC:
			mne = KHMNE_DEC;
			break;

		default:
			return FALSE;
	}

	kh_cd_emit(code, token->line, mne);

	if (!token->parent)
		return FALSE;
	else
	{
		// assign에서 처리하기 때문에 여기서 빼도록한다.
		/*
		khToken* u=token->parent->parent;
		if (u && u->type==KHTKT_OP) kh_cd_emit(code, token->line, KHMNE_DUP);
		*/
		return TRUE;
	}
}

// 연산자 두개항 'binary'
static kcham _kh_cmpl_op_binary(khToken* token, khCodeData* code)
{
	kuint mne;

	if (!_kh_cmpl_compile(token->child[0], code, TRUE) ||
		!_kh_cmpl_compile(token->child[1], code, TRUE) ||
		!_kh_cmpl_check_expr(token, TRUE))
		return FALSE;

	switch (token->role)
	{
		case KHTKR_OP_MUL:
			mne = KHMNE_MUL;
			break;

		case KHTKR_OP_DIV:
			mne = KHMNE_DIV;
			break;

		case KHTKR_OP_MOD:
			mne = KHMNE_MOD;
			break;

		case KHTKR_OP_ADD:
			mne = KHMNE_ADD;
			break;

		case KHTKR_OP_SUB:
			mne = KHMNE_SUB;
			break;

		case KHTKR_OP_SHL:
			mne = KHMNE_SHL;
			break;

		case KHTKR_OP_SHR:
			mne = KHMNE_SHR;
			break;

		case KHTKR_OP_LT:
			mne = KHMNE_BLT;
			break;

		case KHTKR_OP_LE:
			mne = KHMNE_BLE;
			break;

		case KHTKR_OP_EQ:
			mne = KHMNE_BEQ;
			break;

		case KHTKR_OP_NEQ:
			mne = KHMNE_BNE;
			break;

		case KHTKR_OP_BINARY_AND:
			mne = KHMNE_AND;
			break;

		case KHTKR_OP_BINARY_XOR:
			mne = KHMNE_XOR;
			break;

		case KHTKR_OP_BINARY_OR:
			mne = KHMNE_OR;
			break;

		default:
			return FALSE;
	}

	kh_cd_emit(code, token->line, mne);

	return TRUE;
}

// 연산자 두개항 논리곱 'binary and'
static kcham _kh_cmpl_op_logical_and(khToken* token, khCodeData* code)
{
	kint n1, n2;

	if (!_kh_cmpl_compile(token->child[0], code, TRUE))
		return FALSE;

	n1 = kh_cd_skip_2(code, token->child[0]->line);

	if (!_kh_cmpl_compile(token->child[1], code, TRUE))
		return FALSE;

	n2 = kh_cd_swap_loc(code, n1);
	kh_cd_emit_addr(code, token->line, KHMNE_T_AND, n2);
	kh_cd_set_loc(code, n2);

	kh_fd_branch_add(_kh_cmpl_cur_func(), n1, TRUE);

	return TRUE;
}

// 연산자 두개항 논리합 'binary or'
static kcham _kh_cmpl_op_logical_or(khToken* token, khCodeData* code)
{
	kint n1, n2;

	if (!_kh_cmpl_compile(token->child[0], code, TRUE))
		return FALSE;

	n1 = kh_cd_skip_2(code, token->child[0]->line);

	if (!_kh_cmpl_compile(token->child[1], code, TRUE))
		return FALSE;

	n2 = kh_cd_swap_loc(code, n1);
	kh_cd_emit_addr(code, token->line, KHMNE_T_OR, n2);
	kh_cd_set_loc(code, n2);

	kh_fd_branch_add(_kh_cmpl_cur_func(), n1, TRUE);

	return TRUE;
}

// 연산자 배열 첨자 'index'
static kcham _kh_cmpl_op_index(khToken* token, khCodeData* code)
{
	if (!_kh_cmpl_compile(token->child[0], code, TRUE) ||
		!_kh_cmpl_compile(token->child[1], code, TRUE))
		return FALSE;

	kh_cd_emit(code, token->line, KHMNE_LDELEM);

	return TRUE;
}

// 연산자 점 'dot'
static kcham _kh_cmpl_op_dot(khToken* token, khCodeData* code)
{
	khToken* child;
	kuint sym;

	if (!_kh_cmpl_compile(token->child[0], code, TRUE))
		return FALSE;

	child = token->child[1];

	if (child && child->type == KHTKT_IDENTIFIER)
	{
		sym = kh_fd_const_sym(_kh_cmpl_cur_func(), child->value.str);
		kh_cd_emit_sym(code, token->line, KHMNE_LDFLD, sym);
		return TRUE;
	}
	else
	{
		kh_cmpl_error(TRUE, token, KERR_INVAL, "invalid dot operator.");
		return FALSE;
	}
}

// 연산자 사이즈 'sizeof'
static kcham _kh_cmpl_op_sizeof(khToken* token, khCodeData* code)
{
	if (!_kh_cmpl_compile(token->child[0], code, TRUE) ||
		!_kh_cmpl_check_expr(token, TRUE))
		return FALSE;

	kh_cd_emit(code, token->line, KHMNE_SIZEOF);

	return TRUE;
}

// 연산자 타입 이름 'typename'
static kcham _kh_cmpl_op_typename(khToken* token, khCodeData* code)
{
	if (!_kh_cmpl_compile(token->child[0], code, TRUE) ||
		!_kh_cmpl_check_expr(token, TRUE))
		return FALSE;

	kh_cd_emit(code, token->line, KHMNE_TYPENAME);

	return TRUE;
}

// 연산자 새로만들기 'new'
static kcham _kh_cmpl_op_new(khToken* token, khCodeData* code)
{
	khToken* arg;
	kuint sym;
	kint cnt;

	// 이름 먼저
	if (token->child[0]->type != KHTKT_IDENTIFIER)
		return FALSE;

	sym = kh_fd_const_sym(_kh_cmpl_cur_func(), token->child[0]->value.str);

	// 인수
	cnt = 0;
	arg = token->child[1];

	while (arg)
	{
		cnt++;

		if (!_kh_cmpl_compile(arg, code, FALSE))
			return FALSE;

		arg = arg->next;
	}

	// 명령
	kh_cd_emit_each(code, token->line, KHMNE_NEWOBJ, cnt, *(kint*)& sym);
	return TRUE;
}

// 구문 묶음 'compound'
static kcham _kh_cmpl_stmt_compound(khToken* token, khCodeData* code)
{
	return _kh_cmpl_compile(token->child[0], code, TRUE);
}

// 구문 반환 'return'
static kcham _kh_cmpl_stmt_return(khToken* token, khCodeData* code)
{
	if (token->child[0])
	{
		if (!_kh_cmpl_compile(token->child[0], code, TRUE))
			return FALSE;

		kh_cd_emit(code, token->line, KHMNE_RET);
	}

	kh_cd_emit(code, token->line, KHMNE_END);

	return TRUE;
}

// 구문 함수 'function'
static kcham _kh_cmpl_stmt_function(khToken* token, khCodeData* code)
{
	khToken* parent;
	khToken* name;
	khToken* arg;
	kint cnt;
	kbool ret;

	khFuncData* cfunc;
	khFuncData* prev;

	kRedFn* rfn;
	kint fnidx;

	prev = _kh_cmpl_cur_func();

	_kh_cmpl_push_func();
	cfunc = _kh_cmpl_cur_func();

	// 함수 이름
	parent = token->parent;

	if (parent->child[1] == token && (parent->role == KHTKR_OP_ASSIGN || parent->role == KHTKR_OP_ASSIGN_FIELD))
	{
		name = parent->child[0];

		if (name)
		{
			if (name->type == KHTKT_IDENTIFIER)
				;
			else if (name->role == KHTKR_OP_DOT)
				name = name->child[1];
			else
				name = NULL;
		}

		if (name)
		{
			// 함수 이름
			cfunc->name = name->value.str;
		}
		else
		{
			kh_cmpl_error(TRUE, token, KERR_EMPTY, "no function name.");
			_kh_cmpl_pop_func();
			return FALSE;
		}
	}
	else
	{
		// 이름 엄따
		name = NULL;
	}

	// 인수
	cnt = 0;
	arg = token->child[0];

	while (arg)
	{
		if (kh_fd_var_set(cfunc, KHSCP_LOCAL, arg->child[0], TRUE, TRUE) != cnt)
		{
			kh_cmpl_error(TRUE, arg->child[0], KERR_ALREADY, "in function '%s', already has argument '%s'.",
				cfunc->name, arg->child[0]->value.str);
			_kh_cmpl_pop_func();
			return FALSE;
		}

		kh_fd_var_mark(cfunc, cnt);
		cnt++;
		arg = arg->next;
	}

	cfunc->args = cnt;

	// 코드
	ret = _kh_cmpl_compile(token->child[1], &cfunc->code, TRUE) && _kh_cmpl_finalize_func(cfunc);

	// 함수 만들기
	if (ret)
	{
		rfn = k_redfn_new();
		k_now(&cfunc->bdt);
		k_redfn_set_code(rfn, cfunc);

		fnidx = kh_fd_const_fn(prev, rfn);

#if 0
		kh_cd_emit_addr(code, (name) ? name->line : parent->line, KHMNE_LDFN, funcindex);
#else
		// store와 줄을 맞추기 위해 compound위치로 바꿈
		kh_cd_emit_addr(code, token->child[1]->line, KHMNE_LDFN, fnidx);
#endif
	}

	_kh_cmpl_pop_func();

	return ret;
}

// 구문 비교 'if'
static kcham _kh_cmpl_stmt_if(khToken* token, khCodeData* code)
{
	kint n1, n2, n3;

	if (token->child[2])
	{
		// else가 있다
		if (!_kh_cmpl_compile(token->child[0], code, TRUE))
			return FALSE;

		n1 = kh_cd_skip_2(code, token->child[0]->line);

		if (!_kh_cmpl_compile(token->child[1], code, TRUE))
			return FALSE;

		n2 = kh_cd_skip_2(code, token->child[1]->line);

		if (!_kh_cmpl_compile(token->child[2], code, TRUE))
			return FALSE;

		n3 = kh_cd_get_loc(code);

		_kh_cmpl_cur_func()->line = token->line;
		kh_cd_set_loc(code, n1);
		kh_cd_emit_addr(code, token->line, KHMNE_BRZ, n2 + 2);		// else의 점프 명령+위치 크기까지(BR+n3)
		kh_cd_set_loc(code, n2);
		kh_cd_emit_addr(code, token->line, KHMNE_T_IFELSE, n3);		// 원래 BR
		kh_cd_set_loc(code, n3);

		// 자세한 내용을 저장
		kh_fd_branch_add(_kh_cmpl_cur_func(), n2, TRUE);
	}
	else
	{
		// else없음
		if (!_kh_cmpl_compile(token->child[0], code, TRUE))
			return FALSE;

		n1 = kh_cd_skip_2(code, token->child[0]->line);

		if (!_kh_cmpl_compile(token->child[1], code, TRUE))
			return FALSE;

		n2 = kh_cd_skip_2(code, token->child[1]->line);

		_kh_cmpl_cur_func()->line = token->line;
		kh_cd_set_loc(code, n1);
		kh_cd_emit_addr(code, token->line, KHMNE_BRZ, n2);
		kh_cd_set_loc(code, n2);
	}

	return TRUE;
}

// 구문 선항 반복문 'while'
static kcham _kh_cmpl_stmt_while(khToken* token, khCodeData* code)
{
	struct khLoopData* ld;
	kint n1, n2, na;

	_kh_cmpl_push_loop(KHLP_WHILE);

	// 조건부
	n1 = na = kh_cd_get_loc(code);

	if (!_kh_cmpl_compile(token->child[0], code, TRUE))
	{
		_kh_cmpl_pop_loop();
		return FALSE;
	}

	// 실행부
	n2 = kh_cd_skip_2(code, token->line);

	if (!_kh_cmpl_compile(token->child[1], code, TRUE))
	{
		_kh_cmpl_pop_loop();
		return FALSE;
	}

	kh_cd_emit_addr(code, token->line, KHMNE_BR, n1);
	n1 = kh_cd_get_loc(code);

	kh_cd_set_loc(code, n2);
	kh_cd_emit_addr(code, token->line, KHMNE_BRZ, n1);
	kh_cd_set_loc(code, n1);

	ld = _kh_cmpl_cur_loop();
	_kh_cmpl_apply_patch(ld->brk, code, n1);
	_kh_cmpl_apply_patch(ld->ctn, code, na);

	_kh_cmpl_pop_loop();

	return TRUE;
}

// 구문 후항 반목문 'do~while'
static kcham _kh_cmpl_stmt_do_while(khToken* token, khCodeData* code)
{
	struct khLoopData* ld;
	kint n1, na;

	_kh_cmpl_push_loop(KHLP_DOWHILE);

	// 실행부
	n1 = kh_cd_get_loc(code);

	if (!_kh_cmpl_compile(token->child[1], code, TRUE))
	{
		_kh_cmpl_pop_loop();
		return FALSE;
	}

	// 조건부
	na = kh_cd_get_loc(code);

	if (!_kh_cmpl_compile(token->child[0], code, TRUE))
	{
		_kh_cmpl_pop_loop();
		return FALSE;
	}

	kh_cd_emit_addr(code, token->line, KHMNE_BRE, n1);
	n1 = kh_cd_get_loc(code);

	ld = _kh_cmpl_cur_loop();
	_kh_cmpl_apply_patch(ld->brk, code, n1);
	_kh_cmpl_apply_patch(ld->ctn, code, na);

	_kh_cmpl_pop_loop();

	return TRUE;
}

// 구문 반복문 'for'
static kcham _kh_cmpl_stmt_for(khToken* token, khCodeData* code)
{
	struct khLoopData* ld;
	kint n1, n2, na;

	// 선언부
	if (!_kh_cmpl_compile(token->child[0], code, TRUE))
		return FALSE;

	_kh_cmpl_push_loop(KHLP_FOR);

	// 조건부
	n1 = kh_cd_get_loc(code);

	if (!_kh_cmpl_compile(token->child[1], code, TRUE))
	{
		_kh_cmpl_pop_loop();
		return FALSE;
	}

	// 실행부
	n2 = (token->child[1] != NULL) ? kh_cd_skip_2(code, token->child[1]->line) : 0;

	if (!_kh_cmpl_compile(token->child[3], code, TRUE))
	{
		_kh_cmpl_pop_loop();
		return FALSE;
	}

	// 루프부
	na = kh_cd_get_loc(code);

	if (!_kh_cmpl_compile(token->child[2], code, TRUE))
	{
		_kh_cmpl_pop_loop();
		return FALSE;
	}

	kh_cd_emit_addr(code, token->line, KHMNE_BR, n1);
	n1 = kh_cd_get_loc(code);

	if (token->child[1] != NULL)
	{
		kh_cd_set_loc(code, n2);
		kh_cd_emit_addr(code, token->line, KHMNE_BRNE, n1);
		kh_cd_set_loc(code, n1);
	}

	ld = _kh_cmpl_cur_loop();
	_kh_cmpl_apply_patch(ld->brk, code, n1);
	_kh_cmpl_apply_patch(ld->ctn, code, na);

	_kh_cmpl_pop_loop();

	return TRUE;
}

// 구문 개별 반복문 'foreach'
static kcham _kh_cmpl_stmt_foreach(khToken* token, khCodeData* code, kbool isdbl)
{
	struct khLoopData* ld;
	kint brk, ctn, l1, l2;
	kint ko, vo;

	// 테이블
	if (!_kh_cmpl_compile(token->child[0], code, TRUE))
		return FALSE;

	_kh_cmpl_push_loop(KHLP_FOREACH);

	// 키
	if (!isdbl)
		ko = KHSCP_UNKNOWN;
	else
	{
		ko = kh_fd_var_set(_kh_cmpl_cur_func(), KHSCP_LOCAL, token->child[2], TRUE, TRUE);

		if (ko == KHSCP_UNKNOWN)
		{
			_kh_cmpl_pop_loop();
			return FALSE;
		}

		kh_fd_var_mark(_kh_cmpl_cur_func(), ko);
	}

	// 값
	vo = kh_fd_var_set(_kh_cmpl_cur_func(), KHSCP_LOCAL, token->child[1], TRUE, TRUE);

	if (vo == KHSCP_UNKNOWN)
	{
		_kh_cmpl_pop_loop();
		return FALSE;
	}

	kh_fd_var_mark(_kh_cmpl_cur_func(), vo);

	// 시작 -> 반복자 설정 및 비교값형 미리 설정
	kh_cd_emit_each(code, token->line, KHMNE_PREPEACH, 0 /*시작위치 기본으로 0부터 시작*/, isdbl ? ko : -1);
	ctn = kh_cd_get_loc(code);

	// 명령
	l1 = kh_cd_get_loc(code);

	if (!isdbl)
		kh_cd_emit_op(code, token->line, KHMNE_LOOPEACH_SGL, vo);
	else
		kh_cd_emit_each(code, token->line, KHMNE_LOOPEACH_DBL, ko, vo);

	l2 = kh_cd_skip_2(code, token->line);

	// 실행부
	if (!_kh_cmpl_compile(token->child[3], code, TRUE))
	{
		_kh_cmpl_pop_loop();
		return FALSE;
	}

	// 점프부
	kh_cd_emit_addr(code, token->line, KHMNE_BR, l1);
	brk = kh_cd_get_loc(code);
	kh_cd_set_loc(code, l2);
	kh_cd_emit_addr(code, token->line, KHMNE_BRNE, brk);
	kh_cd_set_loc(code, brk);

	// 테이블과 반복자 제거
	kh_cd_emit(code, token->line, KHMNE_DISPEACH);

	ld = _kh_cmpl_cur_loop();
	_kh_cmpl_apply_patch(ld->brk, code, brk);
	_kh_cmpl_apply_patch(ld->ctn, code, ctn);

	_kh_cmpl_pop_loop();

	return TRUE;
}

// 구문 분기선택 'switch'
static kcham _kh_cmpl_stmt_switch(khToken* token, khCodeData* code)
{
	// 0=대상, 1=수행
	khToken* node;
	struct khLoopData* ld;
	kint na, ns;
	kint cnt, def;

	// 개수 찾기
	node = token->child[1]->child[0]; // 컴파운드(1)의 노드(0)

	if (!node || (node->role != KHTKR_STMT_CASE && node->role != KHTKR_STMT_DEFAULT))
	{
		kh_cmpl_error(TRUE, node, KERR_EMPTY, "switch block must start with case statement.");
		return FALSE;
	}

	cnt = def = 0;

	while (node)
	{
		if (node->role == KHTKR_STMT_DEFAULT)
			def++;
		else if (node->role == KHTKR_STMT_CASE)
		{
			if (def != 0)
			{
				kh_cmpl_error(TRUE, node, KERR_EMPTY, "do not use case after default.");
				return FALSE;
			}

			cnt++;
		}

		node = node->next;
	}

	if (cnt == 0 && def == 0)
	{
		kh_cmpl_error(TRUE, token, KERR_EMPTY, "no case or default in switch statement.");
		return FALSE;
	}

	if (def > 1)
	{
		kh_cmpl_error(TRUE, token, KERR_EMPTY, "switch must have 1 default statement.");
		return FALSE;
	}

	if (cnt == 0 && def == 1)
	{
		// 디폴트 밖에 없을 때는 나머지 무시
		_kh_cmpl_push_loop(KHLP_SWITCH);
		_kh_cmpl_push_switch(0, TRUE);

		// 실행부
		if (!_kh_cmpl_compile(token->child[1], code, TRUE))
		{
			_kh_cmpl_pop_switch();
			_kh_cmpl_pop_loop();
			return FALSE;
		}

		na = kh_cd_get_loc(code);
		ld = _kh_cmpl_cur_loop();
		_kh_cmpl_apply_patch(ld->brk, code, na);
		// 컨티뉴는 안됨

		_kh_cmpl_pop_switch();
		_kh_cmpl_pop_loop();
	}
	else
	{
		// 시작
		kh_cd_emit_each(code, token->child[0]->line, KHMNE_PREPSELECT, cnt, def);

		// 대상
		if (!_kh_cmpl_compile(token->child[0], code, TRUE))
			return FALSE;

		// 테이블값
		ns = kh_cd_skip_nth(code, token->child[0]->line, cnt * (2 + 2) + 2); // (ldxxx값2 + bsz값2) + (br값2)

		_kh_cmpl_push_loop(KHLP_SWITCH);
		_kh_cmpl_push_switch(ns, FALSE);

		// 실행부
		if (!_kh_cmpl_compile(token->child[1], code, TRUE))
		{
			_kh_cmpl_pop_switch();
			_kh_cmpl_pop_loop();
			return FALSE;
		}

		// 사후처리
		na = kh_cd_get_loc(code);

		if (def == 0)
		{
			// 디폴트가 없는 경우
			kh_cd_set_loc(code, ns + cnt * (2 + 2));
			kh_cd_emit_addr(code, token->line, KHMNE_BR, na);
			kh_cd_set_loc(code, na);
		}

		// 자동 push를 쓰기 때문에 pop해야함
		kh_cd_emit(code, token->line, KHMNE_DISPSELECT);

		ld = _kh_cmpl_cur_loop();
		_kh_cmpl_apply_patch(ld->brk, code, na);
		// 컨티뉴는 안됨

		_kh_cmpl_pop_switch();
		_kh_cmpl_pop_loop();
	}

	return TRUE;
}

// 구문 분기선택 'case'
static kcham _kh_cmpl_stmt_case(khToken* token, khCodeData* code)
{
	khToken* cnst;
	struct khSwitchData* sd;
	kint nc, nt;

	// 스위치 안에 있나?
	sd = _kh_cmpl_cur_switch();

	if (!sd)
	{
		kh_cmpl_error(TRUE, token, KERR_SYNTAX, "invalid case statement.");
		return FALSE;
	}

	// 값이 상수 인가 -> 당연함. yacc에서 이미 처리됨
	cnst = token->child[0];

	// 값을 확인한다. 값이 32비트용인가
	if (cnst->role == KHTKR_CONST_DOUBLE || cnst->role == KHTKR_CONST_LONG)
	{
		kh_cmpl_error(TRUE, cnst, KERR_SYNTAX, "case value do not support 64bit type.");
		return FALSE;
	}

	// 일단 넣을 곳을 비운다 -> 라인넘버 맞춰 채우기
	nt = sd->addr + sd->tbl;
	nc = kh_cd_swap_loc(code, nt);
	kh_cd_skip_nth(code, token->line, 4);

	// 점프값과 상수값을 만든다
	kh_cd_set_loc(code, nt);

	if (!_kh_cmpl_compile(cnst, code, FALSE))
		return FALSE;

	kh_cd_emit_addr(code, token->line, KHMNE_BRSELECT, nc);
	sd->tbl += 4; // 로드값과 bsz값 추가

	// 원래 위치로 복귀해서 계속함
	kh_cd_set_loc(code, nc);

	return TRUE;
}

// 구문 분기 선택 'default'
static kcham _kh_cmpl_stmt_default(khToken* token, khCodeData* code)
{
	struct khSwitchData* sd;
	kint nc;

	// 스위치 안에 있나
	sd = _kh_cmpl_cur_switch();

	if (!sd)
	{
		kh_cmpl_error(TRUE, token, KERR_SYNTAX, "invalid default statement.");
		return FALSE;
	}

	// 위치 저장. 단, default만 있으면 안한다.
	if (!sd->onlydef)
	{
		nc = kh_cd_swap_loc(code, sd->addr + sd->tbl);
		kh_cd_emit_addr(code, token->line, KHMNE_BR, nc);
		kh_cd_set_loc(code, nc);
	}

	return TRUE;
}

// 구문 계속 'continue'
static kcham _kh_cmpl_stmt_continue_break(khToken* token, khCodeData* code, kbool isbreak)
{
	struct khLoopData* ld;

	ld = _kh_cmpl_cur_loop();

	if (!ld)
	{
		kh_cmpl_error(TRUE, token, KERR_NOMEAN, "invalid %s position.", isbreak ? "break" : "continue");
		return FALSE;
	}

	kh_cd_emit(code, token->line, KHMNE_BR);

	if (isbreak)
		ld->brk = _kh_cmpl_add_patch(kh_cd_skip(code, token->line), ld->brk);
	else
		ld->ctn = _kh_cmpl_add_patch(kh_cd_skip(code, token->line), ld->ctn);

	return TRUE;
}

// 구문 이름 'label'
static kcham _kh_cmpl_stmt_label(khToken* token, khCodeData* code)
{
	khToken* node;
	kint addr;

	addr = kh_cd_get_loc(code);

	if (kh_fd_label_set(_kh_cmpl_cur_func(), addr, token->child[0], &node) != addr)
	{
		// 이미 있는 라벨
		kh_cmpl_error(TRUE, token->child[0], KERR_ALREADY, "label '%s' is already defined at line %d.", node->value.str, node->line);
		return FALSE;
	}

	return TRUE;
}

// 구문 이동 'goto'
static kcham _kh_cmpl_stmt_goto(khToken* token, khCodeData* code)
{
	kint addr;

	addr = kh_fd_label_get(_kh_cmpl_cur_func(), token->child[0], NULL);

	if (addr >= 0)
	{
		// 라벨이 있다
		kh_cd_emit_addr(code, token->line, KHMNE_BR, addr);
	}
	else
	{
		// 라벨이 없다. 나중에 찾을 수 있도록 한다
		addr = kh_cd_skip_2(code, token->line);
		kh_fd_label_reserve(_kh_cmpl_cur_func(), addr, token->child[0]);
	}

	return TRUE;
}

// 구문 줄 'line'
static kcham _kh_cmpl_stmt_line(khToken* token, khCodeData* code)
{
	kh_cd_emit(code, token->line, KHMNE_LINE);

	return TRUE;
}

// 구문 중단점 'breakpoint'
static kcham _kh_cmpl_stmt_breakpoint(khToken* token, khCodeData* code)
{
	khToken* mesg;
	kint strindex;

	mesg = token->child[0];

	if (!mesg)
		kh_cd_emit_addr(code, token->line, KHMNE_BREAKPOINT, -1);
	else
	{
		if (mesg->role == KHTKR_CONST_STRING)
		{
			strindex = kh_fd_const_str(_kh_cmpl_cur_func(), mesg->value.str);
			kh_cd_emit_addr(code, token->line, KHMNE_BREAKPOINT, strindex);
		}
		else
		{
			kh_cmpl_error(TRUE, token, KERR_SYNTAX, "breakpoint use string argument only.");
			return FALSE;
		}
	}

	return TRUE;
}

// 구문 중단 'suspend'
static kcham _kh_cmpl_stmt_suspend(khToken* token, khCodeData* code)
{
	if (!token->child[0])
	{
		// 인수가 없으면 널
		kh_cd_emit(code, token->line, KHMNE_LDNULL);
	}
	else
	{
		// 인수 처리
		if (!_kh_cmpl_compile(token->child[0], code, TRUE))
			return FALSE;
	}

	kh_cd_emit(code, token->line, KHMNE_SUSPEND);

	return TRUE;
}

// 수식 자신 'self'
static kcham _kh_cmpl_expr_self(khToken* token, khCodeData* code)
{
	kh_cd_emit(code, token->line, KHMNE_LDSELF);

	return TRUE;
}

// 수식 표 'table'
static kcham _kh_cmpl_expr_table(khToken* token, khCodeData* code)
{
	khToken* fld;
	kint index;
	kuint sym;

	// 만들기
	kh_cd_emit(code, token->line, KHMNE_NEWTABLE);

	// 필드
	index = 0;
	fld = token->child[0];

	while (fld)
	{
		if (fld->role == KHTKR_OP_ASSIGN_FIELD)
		{
			// 이름 지정, 문자열 항목
			sym = kh_fd_const_sym(_kh_cmpl_cur_func(), fld->child[0]->value.str);
			kh_cd_emit_sym(code, fld->child[0]->line, KHMNE_LDSYM, sym);

			if (!_kh_cmpl_compile(fld->child[1], code, TRUE))
				return FALSE;

			kh_cd_emit(code, fld->child[0]->line, KHMNE_TBLELEM);
		}
		else if (fld->role == KHTKR_OP_ASSIGN_INDEX)
		{
			// 순서형, 정수 및 문자열
			if (fld->child[0]->role == KHTKR_CONST_INT)
			{
				// 정수
				index = (kint)fld->child[0]->value.i64;

				if (index >= 0 && index <= 8)
					kh_cd_emit(code, fld->child[0]->line, KHMNE_LDC_I_0 + index);
				else
					kh_cd_emit_32(code, fld->child[0]->line, KHMNE_LDC_I4, index);

				index++;

				if (!_kh_cmpl_compile(fld->child[1], code, TRUE))
					return FALSE;

				kh_cd_emit(code, fld->child[0]->line, KHMNE_TBLELEM);
			}
			else if (fld->child[0]->role == KHTKR_CONST_STRING)
			{
				// 필드와 같게 -> 문자열
				sym = kh_fd_const_sym(_kh_cmpl_cur_func(), fld->child[0]->value.str);
				kh_cd_emit_sym(code, fld->child[0]->line, KHMNE_LDSYM, sym);

				if (!_kh_cmpl_compile(fld->child[1], code, TRUE))
					return FALSE;

				kh_cd_emit(code, fld->child[0]->line, KHMNE_TBLELEM);
			}
			else
			{
				kh_cmpl_error(TRUE, fld->child[0], KERR_SYNTAX, "filed index must use int or string constant.");
				return FALSE;
			}
		}
		else
		{
			// 정수 항목
			if (index >= 0 && index <= 8)
				kh_cd_emit(code, token->line, KHMNE_LDC_I_0 + index);
			else
				kh_cd_emit_32(code, token->line, KHMNE_LDC_I4, index);

			index++;

			if (!_kh_cmpl_compile(fld, code, FALSE))
				return FALSE;

			kh_cd_emit(code, token->line, KHMNE_TBLELEM);
		}

		fld = fld->next;
	}

	return TRUE;
}

// 수식 함수 'function'
static kcham _kh_cmpl_expr_function(khToken* token, khCodeData* code)
{
	khToken* parent;
	khToken* name;
	khToken* arg;
	kint cnt;
	kbool ret;

	khFuncData* cfunc;
	khFuncData* prev;

	kRedFn* rfn;
	kint fnidx;

	prev = _kh_cmpl_cur_func();

	_kh_cmpl_push_func();
	cfunc = _kh_cmpl_cur_func();

	// 함수 이름
	parent = token->parent;

	if (parent->child[1] == token &&
		(parent->role == KHTKR_OP_ASSIGN || parent->role == KHTKR_OP_ASSIGN_FIELD))
	{
		name = parent->child[0];

		if (name)
		{
			if (name->type == KHTKT_IDENTIFIER)
				;
			else if (name->role == KHTKR_OP_DOT)
				name = name->child[1];
			else
				name = NULL;
		}

		if (name)
		{
			// 함수 이름
			cfunc->name = name->value.str;
		}
		else
		{
			// 로보웨어 키미 런타임에서 발생된 오류 중 유추 지점.
			// -> 근데, 여기서 문제될리가 없다...
			kh_cmpl_error(TRUE, token, KERR_EMPTY, "no function name.");
			_kh_cmpl_pop_func();
			return FALSE;
		}
	}
	else
	{
		// 이름 없다.
		name = NULL;
	}

	// 인수
	cnt = 0;
	arg = token->child[0];

	while (arg)
	{
		if (kh_fd_var_set(cfunc, KHSCP_LOCAL, arg->child[0], TRUE, TRUE) != cnt)
		{
			kh_cmpl_error(TRUE, arg->child[0], KERR_ALREADY, "in function '%s', already has argument '%s'.",
				cfunc->name ? cfunc->name : "<unknown>", arg->child[0]->value.str);
			_kh_cmpl_pop_func();
			return FALSE;
		}

		kh_fd_var_mark(cfunc, cnt);
		cnt++;
		arg = arg->next;
	}

	cfunc->args = cnt;

	// 코드
	ret = _kh_cmpl_compile(token->child[1], &cfunc->code, TRUE) && _kh_cmpl_finalize_func(cfunc);

	// 함수 만들기
	if (ret)
	{
		rfn = k_redfn_new();
		k_now(&cfunc->bdt);
		k_redfn_set_code(rfn, cfunc);

		fnidx = kh_fd_const_fn(prev, rfn);

#if 0
		kh_cd_emit_addr(code, (name) ? name->line : parent->line, KHMNE_LDFN, fnidx);
#else
		// store와 줄을 맞추기 위해 compound위치로 바꿈
		kh_cd_emit_addr(code, token->child[1]->line, KHMNE_LDFN, fnidx);
#endif
	}

	_kh_cmpl_pop_func();

	return ret;
}

// 수식 호출 'call'
static kcham _kh_cmpl_expr_call(khToken* token, khCodeData* code)
{
	khToken* node;
	khToken* arg;
	kint cnt;
	kuint sym;

	node = token->child[0];

	if (node->role == KHTKR_OP_DOT)
	{
		// 점 호출
		if (!_kh_cmpl_compile(node->child[0], code, TRUE))
			return FALSE;

		kh_cd_emit(code, token->line, KHMNE_DUP);
		sym = kh_fd_const_sym(_kh_cmpl_cur_func(), node->child[1]->value.str);
		kh_cd_emit_sym(code, token->line, KHMNE_LDFLD, sym);
	}
	else
	{
		if (token->child[2])
		{
			// '@'으로 호출
			if (!_kh_cmpl_compile(token->child[2], code, TRUE))
				return FALSE;
		}
		else
		{
			// 그냥 호출
			kbool isld = FALSE;

			if (node->type == KHTKT_IDENTIFIER)
			{
				struct khVarData* vd;
				int offset;

				offset = kh_fd_var_get(_kh_cmpl_cur_func(), KHSCP_UNKNOWN, node, &vd);
				_kh_cmpl_check_assign(node, offset);

				if (node->role == KHTKR_ID_SELF || (vd && vd->off == KHSCP_SELF))
				{
					kh_cd_emit(code, token->line, KHMNE_LDSELF);
					isld = TRUE;
				}
			}

			if (!isld)
				kh_cd_emit(code, token->line, KHMNE_LDNULL);
		}

		if (!_kh_cmpl_compile(node, code, TRUE))
			return FALSE;
	}

	// 인수
	cnt = 0;
	arg = token->child[1];

	while (arg)
	{
		if (!_kh_cmpl_compile(arg, code, FALSE))
			return FALSE;

		cnt++;
		arg = arg->next;
	}

	// 호출
	kh_cd_emit_op(code, token->line, KHMNE_CALL, cnt);

	if (!_kh_cmpl_check_expr(token, FALSE))
		kh_cd_emit(code, token->line, KHMNE_POP);

	return TRUE;
}

// 테스트
static kcham _kh_cmpl_kw_kshisgood(khToken* token, khCodeData* code)
{
	if (!_kh_cmpl_compile(token->child[0], code, TRUE))
		return FALSE;

	if (!_kh_cmpl_check_expr(token, TRUE))
		return FALSE;

	kh_cd_emit(code, token->line, KHMNE_DONOTUSETHIS);

	return TRUE;
}

// 코드 정리
static kcham _kh_cmpl_finalize_func(khFuncData* fd)
{
	khCodeData* code = &fd->code;
	kint n, t;
	kssize_t i;

	// 브랜치 검사
	for (i = 0; i < k_arr_count(&fd->brcs); i++)
	{
		struct khBranchData* pbr = &k_arr_nth(&fd->brcs, i);

		switch (code->code[pbr->addr])
		{
			case KHMNE_T_AND:
				t = code->code[pbr->addr + 1];

				for (;;)
				{
					n = code->code[t];

					if (n == KHMNE_T_AND || n == KHMNE_BRNE)
						t = code->code[t + 1];
					else if (n == KHMNE_BRZ)
					{
						code->code[pbr->addr] = KHMNE_BRNE;
						code->code[pbr->addr + 1] = code->code[t + 1];
						break;
					}
					else
					{
						code->code[pbr->addr] = KHMNE_BRNE;
						code->code[pbr->addr + 1] = t + 2;
						break;
					}
				}

				break;

			case KHMNE_T_OR:
				t = code->code[pbr->addr + 1];

				for (;;)
				{
					n = code->code[t];

					if (n == KHMNE_T_OR || n == KHMNE_BRE)
						t = code->code[t + 1];
					else
					{
						code->code[pbr->addr] = KHMNE_BRE;
						code->code[pbr->addr + 1] = t + 2;
						break;
					}
				}

				break;

			case KHMNE_T_IFELSE:
				t = pbr->addr;

				for (;;)
				{
					n = code->code[t];

					if (n == KHMNE_BR || n == KHMNE_T_IFELSE)
						t = code->code[t + 1];
					else
					{
						code->code[pbr->addr] = KHMNE_BR;
						code->code[pbr->addr + 1] = t;
						break;
					}
				}
		}
	}

	// 예약 라벨 검사
	for (i = 0; i < k_arr_count(&fd->rsvs); i++)
	{
		struct khLabelData* pld = &k_arr_nth(&fd->rsvs, i);
		kint addr = kh_fd_label_get(fd, pld->token, NULL);

		if (addr < 0)
		{
			kh_cmpl_error(TRUE, pld->token, KERR_NOMEAN, "cannot found label \"%s\".",
				pld->token->value.str);
			return FALSE;
		}
		else
		{
			code->code[pld->addr] = KHMNE_BR;
			code->code[pld->addr + 1] = addr;
		}
	}

	// 라벨 사용 유무 검사
	for (i = 0; i < k_arr_count(&fd->lbls); i++)
	{
		struct khLabelData* pld = &k_arr_nth(&fd->lbls, i);

		if (pld->ref == 0)
			kh_cmpl_error(FALSE, pld->token, KERR_NOREF, "\"%s\" is never referenced.",
			pld->token->value.str);
	}

	// 스트링을 심볼로 업그레이드 검사
	for (i = 0; i < k_parr_count(&fd->reds); i++)
	{
		kRedStr* rs = (kRedStr*)k_parr_nth(&fd->reds, i);

		if (k_base_get_type((kBase*)rs) == KTYPE_STR)
			k_redstr_build_sym(rs, TRUE);
	}

	// 끝 검사
#if 0
	if (kh_cd_get_code_inv(code, 0) != KHMNE_END)
	{
		n = 0;

		for (i = 0; i < (kssize_t)kh_cd_get_loc(code); i++)
		{
			if (n < code->line[i])
				n = code->line[i];
		}

		kh_cd_emit(code, n + 1, KHMNE_END);
	}
#else
	//  [2012-09-17 ksh] [2013-10-05 ksh] [2013-11-18 ksh]
	// 이거 하는 이유는 맨 끝에 'return'이 있으면 END가 두번 필요하다
	// return 이 END+1의 주소를 참조함
	for (n = 0, i = 0; i < (kssize_t)kh_cd_get_loc(code); i++)
	{
		if (n < code->line[i])
			n = code->line[i];
	}

	kh_cd_emit(code, n + 1, KHMNE_END);
#endif

	return TRUE;
}

// 드뎌 껌팔 수 있다
kcham _kh_cmpl_compile(khToken* token, khCodeData* code, kbool sib)
{
	kcham ret = FALSE;
	kint type;

	while (token)
	{
		if (_kh_cmpl_cur_func())
			_kh_cmpl_cur_func()->line = token->line;

		type = token->type;

		switch (type)
		{
			case KHTKT_IDENTIFIER:
				ret = _kh_cmpl_type_identifier(token, code);
				break;

			case KHTKT_CONSTANT:
				ret = _kh_cmpl_type_constant(token, code);
				break;

			case KHTKT_DECL:
				ret = _kh_cmpl_type_declaration(token, code);
				break;

			case KHTKT_STMT:
				switch (token->role)
				{
					case KHTKR_STMT_COMPOUND:
						ret = _kh_cmpl_stmt_compound(token, code);
						break;

					case KHTKR_STMT_RETURN:
						ret = _kh_cmpl_stmt_return(token, code);
						break;

					case KHTKR_STMT_FUNCTION:
						ret = _kh_cmpl_stmt_function(token, code);
						break;

					case KHTKR_STMT_IF:
						ret = _kh_cmpl_stmt_if(token, code);
						break;

					case KHTKR_STMT_WHILE:
						ret = _kh_cmpl_stmt_while(token, code);
						break;

					case KHTKR_STMT_DO:
						ret = _kh_cmpl_stmt_do_while(token, code);
						break;

					case KHTKR_STMT_FOR:
						ret = _kh_cmpl_stmt_for(token, code);
						break;

					case KHTKR_STMT_FOREACH_DBL:
						ret = _kh_cmpl_stmt_foreach(token, code, TRUE);
						break;

					case KHTKR_STMT_FOREACH_SGN:
						ret = _kh_cmpl_stmt_foreach(token, code, FALSE);
						break;

					case KHTKR_STMT_SWITCH:
						ret = _kh_cmpl_stmt_switch(token, code);
						break;

					case KHTKR_STMT_CASE:
						ret = _kh_cmpl_stmt_case(token, code);
						break;

					case KHTKR_STMT_DEFAULT:
						ret = _kh_cmpl_stmt_default(token, code);
						break;

					case KHTKR_STMT_CONTINUE:
						ret = _kh_cmpl_stmt_continue_break(token, code, FALSE);
						break;

					case KHTKR_STMT_BREAK:
						ret = _kh_cmpl_stmt_continue_break(token, code, TRUE);
						break;

					case KHTKR_STMT_LABEL:
						ret = _kh_cmpl_stmt_label(token, code);
						break;

					case KHTKR_STMT_GOTO:
						ret = _kh_cmpl_stmt_goto(token, code);
						break;

					case KHTKR_STMT_LINE:
						ret = _kh_cmpl_stmt_line(NULL, code);
						break;

					case KHTKR_STMT_BREAKPOINT:
						ret = _kh_cmpl_stmt_breakpoint(token, code);
						break;

					case KHTKR_STMT_SUSPEND:
						ret = _kh_cmpl_stmt_suspend(token, code);
						break;

					default:
						kh_cmpl_error(TRUE, token, KERR_INVAL, "'%d' is not valid statement.", token->role);
						return FALSE;
				}

				break;

			case KHTKT_EXPR:
				switch (token->role)
				{
					case KHTKR_EXPR_SELF:
						ret = _kh_cmpl_expr_self(token, code);
						break;

					case KHTKR_EXPR_TABLE:
						ret = _kh_cmpl_expr_table(token, code);
						break;

					case KHTKR_EXPR_ARRAY:
						// 테이블로 대체
						ret = _kh_cmpl_expr_table(token, code);
						break;

					case KHTKR_EXPR_FUNCTION:
						ret = _kh_cmpl_expr_function(token, code);
						break;

					case KHTKR_EXPR_CALL:
						ret = _kh_cmpl_expr_call(token, code);
						break;

					default:
						kh_cmpl_error(TRUE, token, KERR_INVAL, "'%d' is not valid expression.", token->role);
						return FALSE;
				}

				break;

			case KHTKT_OP:
				switch (token->role)
				{
					case KHTKR_OP_ASSIGN:
						ret = _kh_cmpl_op_assign(token, code);
						break;

					case KHTKR_OP_UNARY_PLUS:
					case KHTKR_OP_UNARY_MINUS:
					case KHTKR_OP_UNARY_NOT:
					case KHTKR_OP_UNARY_SIG:
						ret = _kh_cmpl_op_unary(token, code);
						break;

					case KHTKR_OP_UNARY_INC:
					case KHTKR_OP_UNARY_DEC:
						ret = _kh_cmpl_op_inc_dec(token, code);
						break;

					case KHTKR_OP_MUL:
					case KHTKR_OP_DIV:
					case KHTKR_OP_MOD:
					case KHTKR_OP_ADD:
					case KHTKR_OP_SUB:
					case KHTKR_OP_SHL:
					case KHTKR_OP_SHR:
					case KHTKR_OP_LT:
					case KHTKR_OP_LE:
					case KHTKR_OP_EQ:
					case KHTKR_OP_NEQ:
					case KHTKR_OP_BINARY_AND:
					case KHTKR_OP_BINARY_XOR:
					case KHTKR_OP_BINARY_OR:
						ret = _kh_cmpl_op_binary(token, code);
						break;

					case KHTKR_OP_AND:
						ret = _kh_cmpl_op_logical_and(token, code);
						break;

					case KHTKR_OP_OR:
						ret = _kh_cmpl_op_logical_or(token, code);
						break;

					case KHTKR_OP_INC:
					case KHTKR_OP_DEC:
						kh_cmpl_error(TRUE, token, KERR_NOTIMPL, "not implemented yet. use unary operator.");
						return FALSE;

					case KHTKR_OP_INDEX:
						ret = _kh_cmpl_op_index(token, code);
						break;

					case KHTKR_OP_DOT:
						ret = _kh_cmpl_op_dot(token, code);
						break;

					case KHTKR_OP_SIZEOF:
						ret = _kh_cmpl_op_sizeof(token, code);
						break;

					case KHTKR_OP_TYPENAME:
						ret = _kh_cmpl_op_typename(token, code);
						break;

					case KHTKR_OP_NEW:
						ret = _kh_cmpl_op_new(token, code);
						break;

					default:
						kh_cmpl_error(TRUE, token, KERR_INVAL, "'%d' is not valid operand.", token->role);
						return FALSE;
				}

				break;

			case KHTKT_KEYWORD:
				switch (token->role)
				{
					case KHTKR_KW_DONOTUSETHIS:
						ret = _kh_cmpl_kw_kshisgood(token, code);
						break;

					default:
						kh_cmpl_error(TRUE, token, KERR_INVAL, "'%d' is not valid keyword.", token->role);
						return FALSE;
				}

				break;

			case KHTKT_RESERVED:
				kh_cmpl_error(TRUE, token, KERR_NOTIMPL, "found reserved keyword. but not implemented.");
				return FALSE;
		}

		if (!ret)
			return FALSE;

		token = (sib) ? token->next : NULL;
	}

	return TRUE;
}

//////////////////////////////////////////////////////////////////////////
// 엔터 리브는 여깃지~

// 들어가기
kint kh_compile_enter(khToken* token, const char* name, kRedFn** retfn)
{
	kh_impl_cmpl.error = 0;

	kh_impl_cmpl.cloop = -1;
	kh_impl_cmpl.cswit = -1;
	kh_impl_cmpl.cscope = KHSCP_LOCAL;
	kh_impl_cmpl.cfunc = NULL;

	// 함수 만들기
	_kh_cmpl_push_func();
	kh_impl_cmpl.cfunc->name = name ? name : "@startup";

	// 본격 껌팔기
	if (!_kh_cmpl_compile(token, &kh_impl_cmpl.cfunc->code, TRUE) ||
		!_kh_cmpl_finalize_func(kh_impl_cmpl.cfunc))
	{
		kh_impl_cmpl.error++;
		kh_fd_reset_consts(kh_impl_cmpl.cfunc);
	}
	else
	{
		*retfn = k_redfn_new();
		k_now(&kh_impl_cmpl.cfunc->bdt);
		k_redfn_set_code(*retfn, kh_impl_cmpl.cfunc);
	}

	// 종료 수순
	_kh_cmpl_pop_func();

	if (kh_impl_cmpl.error != 0)
		kh_compile_leave();

	return kh_impl_cmpl.error;
}

// 나가기
void kh_compile_leave(void)
{
	k_arr_clear(khPatchArr, &kh_impl_cmpl.patchs);
	k_arr_clear(khLoopArr, &kh_impl_cmpl.loops);
	k_arr_clear(khSwitchArr, &kh_impl_cmpl.swits);
}

